﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetickEngine
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

#nullable disable
namespace Netick;

/// <summary>
/// The NetickEngine class is the core of Netick, managing networking, simulation, interpolation, replication, etc.
/// </summary>
public sealed class NetickEngine
{
  public bool IsProfilingEnabled;
  public bool IsLoggingEnabled;
  public bool IsLagCompensationDebugEnabled;
  internal bool IsMultithreadingEnabled;
  internal bool AggressivePreAllocation;
  internal bool FastSerialization;
  internal bool IsARM32Bit;
  internal IGameEngine _gameEngine;
  internal NetickConfigData _config;
  internal ReflectionData _reflectionData;
  internal NetworkTransport _transport;
  internal NetworkPeer _peer;
  internal Client _client;
  internal Server _server;
  internal RpcManager _rpcManager;
  internal NetickWorker[] _workers;
  internal NetickWorker _mainWriter;
  internal InternalReplicator _internalReplicator;
  internal EntityMetaReplicator _entityMetaReplicator;
  internal PlayersReplicator _playersReplicator;
  internal InterestManagement _IM;
  internal SimulationClock _timer;
  internal Simulation _sim;
  internal ClientSimulation _cliSim;
  internal ServerSimulation _srvSim;
  internal LocalInterpolation _localInterpolation;
  internal RemoteInterpolation _remoteInterpolation;
  internal INetickComponent _lagComp;
  internal AdaptiveReplicator _adaptiveReplicator;
  internal Monitor _profiler;
  internal Tick _snapshotTick = new Tick(10);
  internal Tick _simulationTick = new Tick(10);
  internal bool _simulate = true;
  internal Random _rand;
  internal bool _isIMEnabled;
  internal int _connectionUserHeaderSizeWords;
  internal bool _lastFrameATickWasExecuted;
  internal bool _isInvokingNetworkRender;
  internal bool _simulateClientsMode;
  public readonly List<IClientSimulationCallbacks> ClientSimulationCallbacks = new List<IClientSimulationCallbacks>(512 /*0x0200*/);
  public float SimulatedLossOut;
  public float SimulatedLossIn;
  private readonly IReadOnlyList<ServerConnection> Empty = (IReadOnlyList<ServerConnection>) new List<ServerConnection>();
  private readonly IReadOnlyList<NetworkPlayer> Empty2 = (IReadOnlyList<NetworkPlayer>) new List<NetworkPlayer>();

  public object UserObject { get; set; }

  public string Name { get; private set; }

  public NetickStartMode StartMode { get; private set; }

  public bool IsRunning { get; private set; }

  public bool IsServer { get; private set; }

  public bool IsClient { get; private set; }

  public int Port { get; private set; }

  public int MaxClients { get; private set; }

  public int MaxPlayers { get; private set; }

  public bool IsInputEnabled
  {
    get => this._sim._inputEnabled;
    set => this._sim.ChangeInputEnabled(value);
  }

  public void Start(
    object userObject,
    string name,
    int serverPort,
    NetickStartMode startMode,
    IGameEngine gameEngine,
    NetickConfigData config,
    NetworkTransport transport,
    ReflectionData reflectionData,
    INetickLogger logger,
    IMemoryAllocator allocator,
    MismatchChecker mismatchChecker,
    int numberOfThreads,
    bool aggressivePreAllocation,
    bool fastSerialization,
    bool isArm32Bit,
    bool isProfilingEnabled,
    bool isLoggingEnabled)
  {
    NetickLogger.Logger = logger;
    MemoryAllocation.Allocator = allocator;
    this.UserObject = userObject;
    this.Name = name;
    this._gameEngine = gameEngine;
    this._reflectionData = reflectionData;
    this._config = config;
    this._transport = transport;
    this.IsProfilingEnabled = isProfilingEnabled;
    this.IsLoggingEnabled = isLoggingEnabled;
    this.IsMultithreadingEnabled = numberOfThreads > 0;
    this.IsARM32Bit = isArm32Bit;
    this.AggressivePreAllocation = aggressivePreAllocation;
    this.FastSerialization = fastSerialization;
    this.IsRunning = true;
    this.IsServer = startMode != NetickStartMode.Client;
    this.IsClient = !this.IsServer;
    this.StartMode = startMode;
    this.Port = serverPort;
    this._rand = new Random();
    this._isIMEnabled = this.Config.EnableInterestManagement;
    this._connectionUserHeaderSizeWords = gameEngine.GetConnectionMetaSizeWords();
    this.MaxClients = config.MaxPlayers - (startMode == NetickStartMode.Host ? 1 : 0);
    if (startMode == NetickStartMode.SinglePlayer)
      this.MaxClients = 1;
    this.MaxPlayers = startMode != NetickStartMode.SinglePlayer ? config.MaxPlayers : 1;
    this._timer = new SimulationClock(this);
    this._rpcManager = new RpcManager(this);
    this._profiler = new Monitor(this);
    this._srvSim = this.IsServer ? new ServerSimulation(this) : (ServerSimulation) null;
    this._cliSim = !this.IsServer ? new ClientSimulation(this) : (ClientSimulation) null;
    this._IM = new InterestManagement(this);
    this._server = this.IsServer ? new Server(this) : (Server) null;
    this._client = !this.IsServer ? new Client(this) : (Client) null;
    this._sim = this.IsServer ? (Simulation) this._srvSim : (Simulation) this._cliSim;
    this._peer = this.IsServer ? (NetworkPeer) this._server : (NetworkPeer) this._client;
    this._entityMetaReplicator = new EntityMetaReplicator(this);
    this._playersReplicator = new PlayersReplicator(this);
    this._internalReplicator = new InternalReplicator(this, this._entityMetaReplicator, this._playersReplicator, mismatchChecker);
    this._entityMetaReplicator = this._internalReplicator.EntityMetaReplicator;
    this._playersReplicator = this._internalReplicator.PlayersReplicator;
    this._localInterpolation = new LocalInterpolation(this);
    this._remoteInterpolation = new RemoteInterpolation(this);
    if (this._reflectionData.LagCompType != (Type) null && this.Config.EnableLagCompensation)
    {
      this._lagComp = Activator.CreateInstance(this._reflectionData.LagCompType) as INetickComponent;
      this._lagComp.Init(this);
    }
    if (this._config.EnableSpatialPrioritization)
      this._adaptiveReplicator = new AdaptiveReplicator(this);
    if (this.IsServer && this._simulateClientsMode)
      this.InitSimulateClients();
    if (this.IsServer)
    {
      this._mainWriter = new NetickWorker(this, true);
      if (this.IsMultithreadingEnabled && numberOfThreads > 0)
      {
        this._workers = new NetickWorker[numberOfThreads];
        for (int index = 0; index < this._workers.Length; ++index)
          this._workers[index] = new NetickWorker(this);
      }
    }
    this._peer.Start(this.Port);
  }

  public void Shutdown()
  {
    this._sim.DrainPendingDestroyedObjects();
    this._rpcManager.Reset();
    this._client?.Stop();
    this._server?.Stop();
    this._client = (Client) null;
    this._server = (Server) null;
    this._peer = (NetworkPeer) null;
    this.IsRunning = false;
    if (!this.IsServer || !this.IsMultithreadingEnabled)
      return;
    for (int index = 0; index < this._workers.Length; ++index)
    {
      this._workers[index].Signaler.Set();
      this._workers[index].Thread.Join();
    }
  }

  public void ResetForSceneSwitch()
  {
    this._lagComp?.Reset();
    this._timer.Reset();
    if (this.IsServer)
      this._sim.Reset();
    else
      this._cliSim.ResetSimulationData();
    this._remoteInterpolation.Reset();
  }

  /// <summary>Updates Netick logic.</summary>
  public void Update(float deltaTime, float timeScale, bool simulate)
  {
    if (!this.IsRunning)
      return;
    this._simulate = simulate;
    this._profiler.UpdateEntryPoint?.Begin();
    this._profiler.FPS?.Update(1f / deltaTime);
    this._timer.PreUpdate(deltaTime, timeScale);
    if (this.StartMode != NetickStartMode.SinglePlayer)
    {
      this.Receive();
      this._peer.Update();
    }
    if (this.IsServer || this.IsClient && this._cliSim.SimulationReady && this.IsConnected)
    {
      if (this.IsInputEnabled & (this.IsClient || this.StartMode == NetickStartMode.Host || this.StartMode == NetickStartMode.SinglePlayer))
        this._gameEngine.OnInput();
      if (!this.IsInputEnabled)
        this._sim.ResetInputProviders();
      if (this.Config.InvokeNetworkUpdate && this._simulate)
      {
        this._profiler.UpdateProfiler?.Begin();
        Action preNetworkUpdate = this.PreNetworkUpdate;
        if (preNetworkUpdate != null)
          preNetworkUpdate();
        this._sim.SimulatedEntities.ExecuteNetworkUpdate();
        Action postNetworkUpdate = this.PostNetworkUpdate;
        if (postNetworkUpdate != null)
          postNetworkUpdate();
        this._profiler.UpdateProfiler?.End();
      }
    }
    this._timer.Update(this._config.ServerDivisor);
    this._sim.DrainPendingBehaviours();
    this._profiler.UpdateEntryPoint?.End();
  }

  internal void Step()
  {
    if (!this.IsRunning || this.IsClient && !this.IsConnected)
      return;
    this._profiler.TickProfiler?.Begin();
    if (this.Timer.IsFirstCall)
      this._lastFrameATickWasExecuted = true;
    if (this.IsServer && this._isIMEnabled)
      this._IM.ResetIM();
    Action onTickStart = this.OnTickStart;
    if (onTickStart != null)
      onTickStart();
    this._sim.Step();
    ++this._simulationTick;
    if (this.IsServer)
    {
      this._playersReplicator.DispatchCallbacks();
      this._sim.DrainPendingSpawnedObjects();
      this._gameEngine.SyncTransformsToColliders();
      this._sim.InvokeGameEngineIntoNetcode();
      this._IM.ProcessChanges();
      this._sim.DrainPendingDestroyedObjects();
    }
    Action onTickEnd = this.OnTickEnd;
    if (onTickEnd != null)
      onTickEnd();
    this.ServerSimulation?.OnTickEnd();
    if (this._timer.IsLastCall && this.StartMode != NetickStartMode.SinglePlayer)
      this.Send();
    this._sim.PostStep();
    this._profiler.TickProfiler?.End();
  }

  private void Send()
  {
    bool shouldSend = (double) this.SimulatedLossOut <= 0.0 || this._rand.NextDouble() >= (double) this.SimulatedLossOut;
    if (this.IsServer)
    {
      if (this._simulationTick % this.Config.ServerDivisor != 0)
        return;
      this._adaptiveReplicator?.Update();
      this._IM.CalcAoI();
      this._profiler.SendProfiler?.Begin();
      Serialization.SendServerPackets(this, shouldSend);
      this._profiler.SendProfiler?.End();
    }
    else
    {
      this._profiler.SendProfiler?.Begin();
      Serialization.SendClientPackets(this, shouldSend);
      this._profiler.SendProfiler?.End();
    }
  }

  private void Receive()
  {
    this._profiler.ReceiveProfiler?.Begin();
    this._peer?.TryReceive();
    if (this._simulateClientsMode)
      this.SimulateAck();
    this._profiler.ReceiveProfiler?.End();
  }

  /// <summary>
  /// Call this to invoke NetworkRender on simulated entities. This might not be needed if you are running a headless build.
  /// </summary>
  public void Render()
  {
    if (!this.IsRunning)
      return;
    this._isInvokingNetworkRender = true;
    this._localInterpolation.Update();
    if (this.IsClient)
    {
      this._remoteInterpolation.Update(this._config.ServerDivisor);
      this._adaptiveReplicator?.Render();
    }
    if ((this.IsServer || this._cliSim != null && this.IsConnected) && this._simulate)
    {
      this._profiler.RenderProfiler?.Begin();
      Action preNetworkRender = this.PreNetworkRender;
      if (preNetworkRender != null)
        preNetworkRender();
      this._sim.SimulatedEntities.ExecuteNetworkRender();
      Action postNetworkRender = this.PostNetworkRender;
      if (postNetworkRender != null)
        postNetworkRender();
      this._profiler.RenderProfiler?.End();
    }
    this._isInvokingNetworkRender = false;
  }

  public NetickConfigData Config => this._config;

  public NetworkTransport Transport => this._transport;

  public InterestManagement InterestManagement => this._IM;

  public NetworkPeer LocalPeer => this._peer;

  public NetworkPeer LocalPlayer => this._peer;

  public Client Client => this._client;

  public Server Server => this._server;

  public Tick SnapshotTick => this._snapshotTick;

  public Tick SimulationTick => this._simulationTick;

  public ClientSimulation ClientSimulation => this._cliSim;

  public ServerSimulation ServerSimulation => this._srvSim;

  public Simulation Simulation => this._sim;

  public INetickComponent LagCompensation => this._lagComp;

  public SimulationClock Timer => this._timer;

  public LocalInterpolation LocalInterpolation => this._localInterpolation;

  public RemoteInterpolation RemoteInterpolation => this._remoteInterpolation;

  public IReadOnlyDictionary<int, Entity> AllEntities
  {
    get => (IReadOnlyDictionary<int, Entity>) this._sim.AllEntities;
  }

  public IReadOnlyDictionary<int, Entity> Entities
  {
    get => (IReadOnlyDictionary<int, Entity>) this._sim.ActiveEntities;
  }

  public IReadOnlyList<NetworkPlayerId> Players => this._playersReplicator.ConnectedPlayers;

  /// <summary>
  /// <c><b>[Client Only]</b></c> The <see cref="T:System.Net.IPEndPoint" /> of the server you are connected to. Returns null if you are not connected to any server, or if you are the server.
  /// </summary>
  public IEndPoint ServerEndPoint
  {
    get
    {
      return !this.IsClient ? (IEndPoint) null : this.Client.ConnectedServer.TransportConnection.EndPoint;
    }
  }

  /// <summary>
  /// <c><b>[Client Only]</b></c> Returns true if this client is currently connected to a server.
  /// </summary>
  public bool IsConnected => this.IsClient && this._client.IsConnected;

  /// <summary>Time period between simulation ticks.</summary>
  public float FixedDeltaTime => this._timer.FixedDelta;

  public Tick Tick => !this.IsServer ? this._cliSim.CurrentTick : this._simulationTick;

  public Tick AuthoritativeTick
  {
    get => !this.IsServer ? this._cliSim.LastAuthoritativeTick : this._simulationTick;
  }

  public Tick PredictedTick => !this.IsServer ? this._cliSim.PredictedTick : this._simulationTick;

  public bool IsFirstNetworkFixedUpdateCall => this.Timer.IsFirstCall;

  public bool IsLastNetworkFixedUpdateCall => this.Timer.IsLastCall;

  /// <summary>
  /// Returns true if we are currently resimulating a previous input/tick of the past. On the server, it always returns false since <b>only the clients resimulate</b>.
  /// </summary>
  public bool IsResimulating => !this.IsServer && this._cliSim.IsResimulating;

  /// <summary>Incoming data in kilobytes per second (KBps).</summary>
  public float InKBps => this.LocalPeer.InKBps;

  /// <summary>Outgoing data in kilobytes per second (KBps).</summary>
  public float OutKBps => this.LocalPeer.OutKBps;

  /// <summary>
  /// <c><b>[Client Only]</b></c> Estimation of incoming packets that are being lost. Value ranges between 0.0 and 1.0, with 1.0 meaning all packets are being lost.
  /// </summary>
  public float InPacketLoss => !this.IsServer ? this.Client.ConnectedServer.InPacketLoss : 0.0f;

  /// <summary>
  /// <c><b>[Client Only]</b></c> Estimation of outgoing packets that are being lost. Value ranges between 0.0 and 1.0, with 1.0 meaning all packets are being lost.
  /// </summary>
  public float OutPacketLoss => !this.IsServer ? this.Client.ConnectedServer.OutPacketLoss : 0.0f;

  /// <summary>
  /// <c><b>[Client Only]</b></c> Interpolation delay in seconds.
  /// </summary>
  public float InterpolationDelay
  {
    get
    {
      return !this.IsClient ? 0.0f : NetickUtils.Clamp01(this.RemoteInterpolation.BufferTime - this.FixedDeltaTime);
    }
  }

  /// <summary>
  /// <c><b>[Client Only]</b></c> The round-trip time (RTT) of the client in seconds.
  /// </summary>
  public double RTT => !this.IsClient ? 0.0 : this.Client.ConnectedServer.RTT.Average;

  public Monitor Monitor => this._profiler;

  public NetworkConnection CurrentRpcSource { get; internal set; }

  public NetworkPlayer CurrentRpcCaller
  {
    get
    {
      return this.CurrentRpcSource != null ? (NetworkPlayer) this.CurrentRpcSource : (NetworkPlayer) this.LocalPeer;
    }
  }

  public bool IsInvokingNetworkRender => this._isInvokingNetworkRender;

  public bool IsIMEnabled => this._isIMEnabled;

  public Action PostNetworkUpdate { get; set; }

  public Action PreNetworkUpdate { get; set; }

  public Action PostNetworkRender { get; set; }

  public Action PreNetworkRender { get; set; }

  public Action PostNetworkFixedUpdate { get; set; }

  public Action PreNetworkFixedUpdate { get; set; }

  public Action PostResimulation { get; set; }

  public Action PreRollback { get; set; }

  public Action PostRollback { get; set; }

  public Action OnPostNetworkStart { get; set; }

  public Action OnTickEnd { get; set; }

  public Action OnTickStart { get; set; }

  internal int SnapshotCutOff => this.Config.MaxSnapshots - 5;

  /// <summary>
  /// A list containing all connected clients currently.
  /// <para>Note: if you want the clients + the server, use <see cref="P:Netick.NetickEngine.ConnectedPlayers" />.</para>
  /// </summary>
  [Obsolete("This will be removed in the future, please use Players instead.")]
  public IReadOnlyList<ServerConnection> ConnectedClients
  {
    get
    {
      return !this.IsServer ? this.Empty : (IReadOnlyList<ServerConnection>) this.Server.ConnectedClients;
    }
  }

  /// <summary>
  /// <para>A list containing all connected clients currently, in addition to the server.</para>
  /// <para>Note: if you only want the clients, use <see cref="P:Netick.NetickEngine.ConnectedClients" />.</para>
  /// </summary>
  [Obsolete("This will be removed in the future, please use Players instead.")]
  public IReadOnlyList<NetworkPlayer> ConnectedPlayers
  {
    get
    {
      return !this.IsServer ? this.Empty2 : (IReadOnlyList<NetworkPlayer>) this.Server.ConnectedPlayers;
    }
  }

  public void Connect(int port, string ip, byte[] connectionData = null, int connectionDataLength = 0)
  {
    if (this.IsServer)
      throw new NotClientException();
    this._client.Connect(port, ip, connectionData, connectionDataLength);
  }

  public void Kick(ServerConnection connection)
  {
    if (this.IsClient)
      throw new NotServerException();
    this._server.Kick(connection);
  }

  /// <summary>
  /// <c><b>[Client Only]</b></c> Disconnects this client from the server.
  /// </summary>
  public void DisconnectFromServer()
  {
    if (!this.IsClient || !this.IsConnected)
      return;
    this._client.Disconnect();
  }

  public void ConnectHost()
  {
    if (this.IsClient)
      return;
    this._gameEngine.OnPlayerConnected((NetworkPlayer) this.LocalPlayer);
    this._playersReplicator.ConnectHostPlayer();
  }

  public void DisconnectHost()
  {
    if (this.IsClient || !this.Server.ConnectedPlayers.Contains((NetworkPlayer) this.LocalPlayer))
      return;
    this.Server.ConnectedPlayers.Remove((NetworkPlayer) this.LocalPlayer);
    this._gameEngine.OnPlayerDisconnected((NetworkPlayer) this.LocalPlayer, TransportDisconnectReason.Shutdown);
    this._gameEngine.OnPlayerLeft(this.LocalPlayer.PlayerId);
  }

  /// <summary>
  /// Returns the size of the entirety of the game's networked state snapshot size, in bytes. This includes the sizes of all allocated blocks.
  /// </summary>
  /// <returns></returns>
  public long GetWorldStateTotalSize()
  {
    List<NetickAllocator.MemoryPool> blocks = this._sim.StateAllocator._allocator.Blocks;
    long worldStateTotalSize = 0;
    for (int index = 0; index < blocks.Count; ++index)
      worldStateTotalSize += blocks[index].ByteSize;
    return worldStateTotalSize;
  }

  public long GetWorldStateBlockSize() => this._sim.StateAllocator._allocator._poolSize;

  public int GetWorldStateBlockCount() => this._sim.StateAllocator._allocator.Blocks.Count;

  public unsafe void CopyWorldStateTo(int blockIndex, byte* target)
  {
    NetickAllocator.MemoryPool block = this._sim.StateAllocator._allocator.Blocks[blockIndex];
    long poolSize = this._sim.StateAllocator._allocator._poolSize;
    MemoryAllocation.Copy((void*) target, block.Ptr, poolSize);
  }

  public unsafe void CopyWorldStateTo(int blockIndex, byte[] target)
  {
    fixed (byte* target1 = target)
      this.CopyWorldStateTo(blockIndex, target1);
  }

  public int GetBehaviourOrder(Type type)
  {
    int behaviourOrder;
    this._reflectionData.ScriptToOrder.TryGetValue(type, out behaviourOrder);
    return behaviourOrder;
  }

  public bool ContainsPlayer(NetworkPlayerId playerId)
  {
    return this._playersReplicator.ContainsPlayer(playerId);
  }

  public NetworkPlayer GetPlayerById(NetworkPlayerId playerId)
  {
    if (playerId.Id < 0)
      return (NetworkPlayer) null;
    if (playerId.Id == 0)
      return this.IsServer ? (NetworkPlayer) this.LocalPlayer : (NetworkPlayer) this.Client.ConnectedServer;
    if (this.IsServer)
      return (NetworkPlayer) this.Server.GetClientByIndex(playerId.Id - 1);
    return this._cliSim.LocalInputSourceIndex == playerId.Id ? (NetworkPlayer) this.LocalPlayer : (NetworkPlayer) null;
  }

  public void GetBehavioursOfType<T>(List<T> result) where T : INetickScript
  {
    if (result == null)
      result = new List<T>(10);
    result.Clear();
    BehaviourList behaviourList;
    if (!this._sim.SimulatedEntities.TypeToBehaviourChain.TryGetValue(typeof (T), out behaviourList))
      return;
    INetickScript[] list = behaviourList.List;
    for (int index = 0; index < behaviourList.Count; ++index)
      result.Add((T) list[index]);
  }

  /// <summary>
  /// Attaches a <see cref="T:Netick.INetickScript" /> to the simulation.
  /// </summary>
  public void AttachBehaviour(INetickScript behaviour, bool addToResimulationLoop = true)
  {
    this._sim.AttachBehaviour(behaviour, addToResimulationLoop);
  }

  /// <summary>
  /// Detaches a <see cref="T:Netick.INetickScript" /> from the simulation.
  /// </summary>
  public void DetachBehaviour(INetickScript behaviour) => this._sim.DetachBehaviour(behaviour);

  public Entity GetPlayerEntity(NetworkPlayerId playerId)
  {
    return this._playersReplicator.GetPlayerEntity(playerId);
  }

  public void SetPlayerEntity(NetworkPlayerId playerId, Entity entity)
  {
    this._playersReplicator.SetPlayerEntity(playerId, entity);
  }

  public Entity CreateEntity(int stateSizeWords) => this._sim.CreateEntity(stateSizeWords);

  public Entity LinkEntity(INetickEntity userEntity, Entity entity)
  {
    return this._sim.LinkEntity(userEntity, entity);
  }

  public Entity CreateAndLinkEntityLocal(INetickEntity userEntity)
  {
    return this._sim.CreateAndLinkEntityLocal(userEntity);
  }

  public void ClientAddUnlinkedEntity(Entity entity, int id, int worldIndex)
  {
    this._sim.ClientAddUnlinkedEntity(entity, id, worldIndex);
  }

  public void ClientAddEntity(
    INetickEntity entity,
    int id,
    int worldIndex,
    bool wasUnliked,
    NetickEntityMeta meta)
  {
    this._sim.AddEntity(entity, worldIndex, id, (NetworkPlayer) null, wasUnliked, meta, true);
  }

  public void ServerAddEntity(INetickEntity obj, NetworkPlayer inputSource, bool invokeAwake)
  {
    this._sim.ServerAddEntity(obj, inputSource, invokeAwake);
  }

  public void DestroyEntity(Entity entity, bool instant, bool freeMemory)
  {
    this._sim.DestroyEntity(entity, instant, freeMemory);
  }

  public Entity GetEntityByWorldIndex(int worldIndex)
  {
    return this._sim.EntitiesWorldIndexArray[worldIndex];
  }

  public unsafe NetickEntityMeta* GetEntityMeta(Entity entity)
  {
    return this._entityMetaReplicator.GetEntityMeta(entity.WorldIndex);
  }

  public unsafe byte* GetEntityUserMeta(Entity entity)
  {
    return this._entityMetaReplicator.GetEntityUserMeta(entity.WorldIndex);
  }

  public unsafe void SetEntityUserMetaDirty(Entity entity, int* userData)
  {
    this._entityMetaReplicator.SetEntityUserMetaDirty(entity.WorldIndex, userData);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public T GetInput<T>(int index = 0) where T : unmanaged => this._sim.GetInput<T>(index);

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public void SetInput<T>(T input, int index = 0) where T : unmanaged
  {
    this._sim.SetInput<T>(input, index);
  }

  [MethodImpl(MethodImplOptions.AggressiveInlining)]
  public bool FetchInput<T>(
    out T input,
    ref bool isDuplicated,
    Entity entity,
    int localPlayerIndex)
    where T : unmanaged
  {
    return this._sim.FetchInput<T>(out input, ref isDuplicated, entity, localPlayerIndex);
  }

  internal void InitSimulateClients()
  {
    for (int index = 0; index < this.MaxClients; ++index)
      this.Server.Clients[index].TransportConnection = (TransportConnection) new DummyTransportConnection();
    for (int index = 0; index < this.MaxClients; ++index)
      this.Server.Clients[index].Reset();
  }

  internal void SimulateAck()
  {
    Entity[] entitiesWorldIndexArray = this.Simulation.EntitiesWorldIndexArray;
    InterestManagementGroup[] allGroups = this._IM.AllGroups;
    if (this.SimulationTick < (Tick) 30)
      return;
    for (short index = 0; (int) index < this._server.Clients.Count; ++index)
    {
      int num = (int) index % 4;
      Tick rawValue = this.SimulationTick - num;
      Tick tick = this.SnapshotTick - num;
      Packet packetRecord = this._server.Clients[(int) index].PacketRecords[tick % this._server.Clients[(int) index].PacketRecords.Length];
      int count = packetRecord.Count;
      NativeArray<ObjectRecord> entities = packetRecord.Entities;
      this._server.Clients[(int) index].BasisTickCur = new Tick((int) rawValue);
      this._server.Clients[(int) index].LastSimTick = new Tick((int) rawValue);
      this._internalReplicator.Entity.AP((int) index, packetRecord.WorldRecord.RangeEnd, packetRecord.WorldRecord.Tick);
      for (int i = 0; i < count; ++i)
      {
        ObjectRecord objectRecord = entities[i];
        if (objectRecord.EntityId != -1)
        {
          if (objectRecord.IsIMGroup == 0)
          {
            Entity entity = entitiesWorldIndexArray[objectRecord.EntityWorldIndex];
            if (entity != null && entity.RawId != -1 && entity.RawId == objectRecord.EntityId)
              entity.AP((int) index, objectRecord.RangeEnd, objectRecord.Tick);
          }
          else
            allGroups[objectRecord.EntityId].Entity.AP((int) index, objectRecord.RangeEnd, objectRecord.Tick);
        }
      }
    }
  }
}
